#include "Tcp.h"
#include <sys/epoll.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <errno.h>
#include <cstdlib>
#include <iostream>

using namespace std;

namespace PFS
{
    Tcp::Tcp():sock_fd(-1)
    {}

    Tcp::Tcp(int fd):sock_fd(fd)
    {}

    Tcp::~Tcp()
    {
    }

    int Tcp::Listen(int port)
    {
        cout<<"listen\n";
        struct sockaddr_in my_addr;
        sock_fd = socket(AF_INET,SOCK_STREAM,0);
        int reuse_addr = 1;
        if(-1 == sock_fd)
        {
            perror("Socket: ");
            return -1;
        }

        memset(&my_addr, 0, sizeof(struct sockaddr_in));
        my_addr.sin_family = AF_INET;
        my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
        my_addr.sin_port = htons(port);

        setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (char*)&reuse_addr,sizeof(reuse_addr));
        if(bind(sock_fd, (struct sockaddr *)&my_addr, sizeof(my_addr)) < 0)
        {
            perror("Bind: ");
            Close();
            return -1;
        }

        if(listen(sock_fd, 1024) < 0)
        {
            perror("Listen: ");
            return -1;
        }

        setup_socket();
        cout<<"listen and bind succ\n";
        return 0;
    }

    void Tcp::Close()
    {
        if(sock_fd < -1)
            return;
        close(sock_fd);
        sock_fd = -1;
    }

    Tcp* Tcp::Accept()
    {
        cout<<"accept\n";
        int fd;
        struct sockaddr_in peer_addr;
        Tcp* in_sock;
        socklen_t peer_len = sizeof(peer_addr);

        if((fd = accept(sock_fd, (struct sockaddr *)&peer_addr, &peer_len)) < 0)
        {
            perror("accept: ");
            return NULL;
        }

        in_sock = new Tcp(fd);
        in_sock->setup_socket();

        return in_sock;
    }

    int Tcp::accept_ret_fd()
    {
        int fd;
        struct sockaddr_in peer_addr;
        socklen_t peer_len = sizeof(peer_addr);

        if((fd = accept(sock_fd, (struct sockaddr *)&peer_addr, &peer_len)) < 0)
        {
            perror("accept: ");
            return NULL;
        }

        Tcp in_sock(fd);
        in_sock.setup_socket();

        return fd;
    }

    void Tcp::setup_socket()
    {
        int buf_size = 65535;
        int flag = 1;

        setsockopt(sock_fd, SOL_SOCKET, SO_SNDBUF, (char *)&buf_size, sizeof(buf_size));
        setsockopt(sock_fd, SOL_SOCKET, SO_RCVBUF, (char *)&buf_size, sizeof(buf_size));
        setsockopt(sock_fd, SOL_SOCKET, SO_KEEPALIVE, (char *)&flag, sizeof(flag));

        fcntl(sock_fd, F_SETFL,fcntl(sock_fd, F_GETFL, 0) | O_NONBLOCK);

        setsockopt(sock_fd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(flag));

    }

    void Tcp::set_fd(int fd)
    {
        sock_fd = fd;
    }
    int Tcp::get_peer_name(struct sockaddr *peer_addr, int len)
    {
        socklen_t peer_len = len;
        if(getpeername(sock_fd, peer_addr, &peer_len) < 0)
        {
            perror("getpeername: ");
            return -1;
        }

        return 0;
    }

    string Tcp::get_peer_name()
    {
        struct sockaddr_in addr;
        char ipname[INET_ADDRSTRLEN + 7];

        if(get_peer_name((SA)&addr, sizeof(struct sockaddr_in)) < 0)
            return "unknown";
        if(inet_ntop(AF_INET, &(addr.sin_addr), ipname, INET_ADDRSTRLEN) == NULL)
            return "unknown";
        ipname[INET_ADDRSTRLEN] = 0;
        sprintf(ipname + strlen(ipname),":%d",(int)ntohs(addr.sin_port));

        return ipname;
    }

    string Tcp::get_sock_name()
    {
        struct sockaddr_in addr;
        char ipname[INET_ADDRSTRLEN + 7];
        socklen_t len = sizeof(struct sockaddr_in);

        if(getsockname(sock_fd,(SA)&addr, &len) < 0)
            return "unknown";
        if(inet_ntop(AF_INET, &(addr.sin_addr), ipname, INET_ADDRSTRLEN) == NULL)
            return "unknown";
        ipname[INET_ADDRSTRLEN] = 0;
        sprintf(ipname + strlen(ipname),":%d",(int)ntohs(addr.sin_port));

        return ipname;

    }

    int Tcp::Send(void* buf, int len)
    {
        size_t nleft;
        ssize_t nwrite;
        char* ptr = (char *)buf;
        nleft = len;

        while(nleft > 0)
        {
            if((nwrite = write(sock_fd,ptr,nleft)) <= 0)
            {
                if(nwrite < 0 && (errno == EINTR || errno == EWOULDBLOCK))
                {
                    nwrite = 0;
                    continue;
                }
                else
                    return -1;
            }
            nleft -= nwrite;
            ptr += nwrite;
        }

        return len;

    }

    int Tcp::Recv(void* buf, int len)
    {
        size_t nleft;
        ssize_t nread;
        char* ptr;

        ptr = (char*)buf;
        nleft = len;
        while(nleft > 0)
        {
            if((nread = read(sock_fd, ptr, nleft)) < 0)
            {
                if(errno == EINTR || errno == EWOULDBLOCK)
                {
                    nread = 0;
                    continue;
                }
                else
                    return -1;
            }
            else if( 0 == nread)
                break;
            nleft -= nread;
            ptr += nread;
        }

        *ptr = 0;
        return (len - nleft);
    }

    int Tcp::peek(void* buf, int len)
    {
        return (len > 0 ? recv(sock_fd, buf, len, MSG_PEEK) : 0);
    }

    TcpServer::TcpServer(int port)
    {
        this->port = port;
        base = event_init();
        job = new EventTask();
    }

    TcpServer::TcpServer(const Tcp &tcp, int port)
    {
        this->port = port;
        base = event_init();
        this->server = Tcp(tcp);
        job = new EventTask();
    }

    TcpServer::~TcpServer()
    {
        if(thread_pool)
            delete thread_pool;
        if(job)
            delete job;

    }
    
    void TcpServer::setup_thread(int num, thread_fun fun)
    {
        thread_pool = new ThreadPool(num, fun);
    }

    int TcpServer::start()
    {
        int nfds, curfds;
        int sock;
        if((server.Listen(port)) == -1)
            return -1;
        sock = server.get_fd(); 
        event_set(&ev, sock, EV_READ | EV_PERSIST, TcpServer::on_new_client, &server);
        event_base_set(base, &ev);
        event_add(&ev, NULL);
        event_base_loop(base , 0);
        return 0;
    }

    void TcpServer::on_new_client(int fd, short flags, void *arg)
    {
       Tcp* server = (Tcp*)arg;
       Tcp* cli = server->Accept();
       return_if_fail(cli != NULL);
       EventConnection *conn = new EventConnection(cli, EV_READ | EV_PERSIST);
       thread_pool.set_task(job, (void*)conn);//choose a thread to add to event
    }

    int TcpServer::stop()
    {
     /*   struct epoll_event ev;
        if(epoll_handler == -1)
            return 0;
        set<int>::iterator it = fd_in_epoll.begin();
        for(;it != fd_in_epoll.end(); it++)
            epoll_ctl(epoll_handler, EPOLL_CTL_DEL, *it, &ev);
        fd_in_epoll.erase(fd_in_epoll.begin(),fd_in_epoll.end());
        */
    }
}
